package com.link.gateway.core.filter.visitstatistics;

import com.link.gateway.core.comproperty.FilterOrderConstants;
import com.link.gateway.core.comproperty.InterfaceLogConstants;
import com.link.gateway.core.filter.visitstatistics.util.ProcessRestrictionVisit;
import com.link.gateway.core.filter.threadpool.ThreadPoolForVisitFilter;
import com.link.gateway.core.filter.visitstatistics.util.VisitConfigSingleton;
import com.link.gateway.core.filter.visitstatistics.util.VisitUtil;
import com.link.gateway.core.redis.redisclient.LinkRedisClient;
import com.link.gateway.core.utils.RedisUtil;
import com.link.gateway.core.utils.ResponseDataUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 接口访问频率过滤器，实现超频访问拦截等功能
 *
 * @author LiYang
 * @date 20190826
 */
@Component
public class VisitFilter implements GlobalFilter, Ordered {

    private static final Logger logger = LogManager.getLogger(VisitFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        VisitConfigSingleton visitConfig = VisitConfigSingleton.getInstance();
        //如果没有开启该功能或没有开启超频拦截，则直接通过
        if (!"Y".equalsIgnoreCase(visitConfig.getStatus())) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        String limitNum = VisitUtil.calcuLimitNumByUrl(request);
        String limitTime = VisitUtil.calcuLimitTimeByUrl(request);
        boolean result = acquire(request, limitNum, limitTime);
        if (!result) {
            ThreadPoolExecutor threadPool = ThreadPoolForVisitFilter.getInstance();
            //新启动一个线程处理超频的访问请求
            threadPool.execute(new ProcessRestrictionVisit(request));
            //拒绝掉该访问请求
            if ("Y".equalsIgnoreCase(visitConfig.getAccessRestriction())) {
                return ResponseDataUtils.writeWith(InterfaceLogConstants.RESTRICTION, "接口超出访问频率限制！", "Y", exchange, null);
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return FilterOrderConstants.VISIT_STATISTICS_ORDER;
    }

    /**
     * 判断该请求是否超出访问频率
     * 返回true表示在正常范围内
     * 返回false表示超出设置的频率
     *
     * @param request
     * @param limitNum
     * @param limitTime
     * @return
     */
    public boolean acquire(ServerHttpRequest request, String limitNum, String limitTime) {
        boolean result = true;
        LinkRedisClient jedis = null;
        //如果为不拦截接口，直接返回true
        if ("N".equals(VisitUtil.calcuLimitNumByUrl(request))) {
            return true;
        }
//        String key = "visit-" + VisitUtil.getUrl(request) + System.currentTimeMillis() / (Long.parseLong(limitTime) * 60 * 1000);
        String key = VisitUtil.getUrl(request);
        List<String> keys = Collections.singletonList(key);
        List<String> argv = Arrays.asList(limitNum, String.valueOf(Long.parseLong(limitTime)));
        try {
            jedis = RedisUtil.getJedis();
//            result = (Long) jedis.eval(VisitUtil.getLuaScript(limitTime), Lists.newArrayList(key), Lists.newArrayList(limitNum)) == 1;
            result = (Long) jedis.eval(VisitUtil.getLuaScript(), keys, argv) == 1;
        } catch (Exception e) {
            logger.error(e);
        } finally {
            RedisUtil.returnResource(jedis);
        }
        return result;
    }

    public static void main(String[] args) {
        Long current = System.currentTimeMillis();
        System.out.println(current);
        Long next = current + 1000 * 60;
        System.out.println(next);
        Long next1 = current + 2 * 60 * 1000;
        System.out.println(next1);
        System.out.println(current / (2 * 60 * 1000));
        System.out.println(next / (2 * 60 * 1000));

    }

}
